package com.zyf.project.binaryproto.converter.impl.fixed;

import com.zyf.project.binaryproto.converter.FixedValueConverter;
import com.zyf.project.binaryproto.exception.DataContractException;
import com.zyf.project.binaryproto.type.PrimitiveType;
import org.jetbrains.annotations.NotNull;
import utils.binary.impl.BytesSlice;

/**
 * <h1>枚举类类型转换器</h1>
 *
 * @author Zhou Yifan
 */
public class EnumValueConverter implements FixedValueConverter {

    /**
     * 枚举类型
     */
    private Class<?> enumType;

    /**
     * 基本类型
     */
    private PrimitiveType codeType;

    /**
     * 编码数组
     */
    private int[] values;

    /**
     * 常量数组
     */
    private Object[] constants;

    /**
     * 静态值转换器
     */
    private FixedValueConverter valueConverter;

    public EnumValueConverter(Class<?> enumType, PrimitiveType codeType, int[] values, Object[] constants, FixedValueConverter valueConverter) {
        this.enumType = enumType;
        this.values = values;
        this.constants = constants;
        this.valueConverter = valueConverter;
        this.codeType = codeType;
    }

    /**
     * 获取枚举类编码
     *
     * @param value     待编码的值
     * @param codes     编码数组
     * @param constants 常量数组
     * @return 编码对象
     */
    private Object getEnumCode(Object value, int[] codes, Object @NotNull [] constants) {
        int codeIndex = 0;

        for (int i = 0; i < constants.length; i++) {
            if (value.toString().equals(constants[i].toString())) {
                codeIndex = i;
                break;
            }
        }
        switch (codeType) {
            case INT8:
                return (byte) codes[codeIndex];
            case INT16:
                return (short) codes[codeIndex];
            case INT32:
                return codes[codeIndex];
            default:
                throw new DataContractException("Enum code error!");
        }
    }

    /**
     * 将把固定长度的值序列化到指定的缓冲区
     * <p>
     * 注：实现者应用确保写入的范围不要越界
     *
     * @param value  要序列化的值
     * @param buffer 保存结果的缓冲区
     * @param offset 缓冲区的写入起始位置
     * @return 返回写入的长度
     */
    @Override
    public int encode(Object value, byte[] buffer, int offset) {
        // 由于是通过反射调用的，已经在外围做了检查，此处不需要重复检查枚举值的范围
        // 首先把枚举常量转换成对应的 CODE
        Object code = getEnumCode(value, values, constants);
        return valueConverter.encode(code, buffer, offset);
    }

    /**
     * 数据片解码
     *
     * @param dataSlice 数据片
     * @return 解码后的对象
     */
    @Override
    public Object decode(BytesSlice dataSlice) {
        Object v = valueConverter.decode(dataSlice);
        switch (codeType) {
            case INT8:
                for (int i = 0; i < values.length; i++) {
                    if ((byte)values[i] == (byte)v) {
                        return constants[i];
                    }
                }
                throw new DataContractException(String.format(
                        "The decoding value is out of all enum constants! --[value=%s][enum type=%s]",
                        v, enumType.toString()
                ));
            case INT16:
                for (int i = 0; i < values.length; i++) {
                    if ((short)values[i] == (short)v) {
                        return constants[i];
                    }
                }
                throw new DataContractException(String.format(
                        "The decoding value is out of all enum constants! --[value=%s][enum type=%s]",
                        v, enumType.toString()
                ));
            case INT32:
                for (int i = 0; i < values.length; i++) {
                    if (values[i] == (int)v) {
                        return constants[i];
                    }
                }
                throw new DataContractException(String.format(
                        "The decoding value is out of all enum constants! --[value=%s][enum type=%s]",
                        v, enumType.toString()
                ));
            default:
                throw new DataContractException("Enum code error");
        }
    }

    /**
     * 获取值的类型
     *
     * @return Class
     */
    @Override
    public Class<?> getValueType() {
        return enumType;
    }

    /**
     * 返回类型的默认初始值；
     *
     * @return Object
     */
    @Override
    public Object getDefaultValue() {
        return null;
    }
}
